package com.itqian.controller.user;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itqian.common.exception.Assert;
import com.itqian.common.exception.EorderException;
import com.itqian.common.exception.ResponseEnum;
import com.itqian.common.utils.JWTUtils;
import com.itqian.common.utils.RegexValidateUtil;
import com.itqian.common.utils.ResultVOUtil;
import com.itqian.common.vo.ResultVO;
import com.itqian.seller.form.StoreIconForm;
import com.itqian.seller.mapper.StoreInfoMapper;
import com.itqian.system.entity.Constants;
import com.itqian.system.entity.User;
import com.itqian.system.form.PhoneLoginForm;
import com.itqian.system.form.ResetPwForm;
import com.itqian.system.form.UserLoginForm;
import com.itqian.system.form.UserRegisterForm;
import com.itqian.system.mapper.UserMapper;
import com.itqian.system.service.UserService;
import com.itqian.system.vo.UserInfoVO;
import com.itqian.system.vo.UserVO;
import com.itqian.utils.TokenUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author shishuqian
 * date 2022/3/21
 * time 14:43
 **/
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private StoreInfoMapper storeInfoMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    @Qualifier("myRedisTemplate")
    private RedisTemplate redisTemplate;

    @Value("${sms.akId}")
    private String akId;

    @Value("${sms.akSecret}")
    private String akSecret;

    @Value("${user.file.path}")
    private String filePath;



    /**
     * 用户注册
     */
    @PostMapping("/register")
    public ResultVO register(@Valid @RequestBody UserRegisterForm form){
        //根据用户名查询用户数据
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username",form.getUsername());
        User one = userService.getOne(wrapper);
        //此用户名如果已被注册
        if (one != null){
            throw new EorderException(ResponseEnum.USER_REGISTERED.getMsg());
        }
        //生成8位随机字符，用做md5盐
        String salt = RandomStringUtils.random(8,true,true);
        //对密码进行md5加盐加密
        String md5 = DigestUtils.md5DigestAsHex((salt + form.getPassword()).getBytes());
        User user = new User();
        user.setUsername(form.getUsername())
                .setPassword(md5)
                .setNickname(form.getNickname())
                .setSalt(salt);
        //保存用户数据到数据库
        boolean save = userService.save(user);
        if (save){
            return ResultVOUtil.success(null);
        }
        return ResultVOUtil.fail("注册失败，请重新注册...");
    }

    /**
     * 用户使用密码登录
     */
    @PostMapping("/login")
    public ResultVO login(@Valid @RequestBody UserLoginForm form){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username",form.getUsername());
        User one = userService.getOne(wrapper);
        //用户名不存在
        if (one == null){
            throw new EorderException(ResponseEnum.USERNAME_NULL.getMsg());
        }

        //验证用户身份权限
        String[] s1 = one.getRole().split("_");
        List<String> roles = Arrays.asList(s1);
        //用户没有对应的身份权限
        if (!roles.contains(form.getRole())){
            throw new EorderException(ResponseEnum.USER_NO_ROLE.getMsg());
        }

        //验证密码是否正确
        String s = DigestUtils.md5DigestAsHex((one.getSalt() + form.getPassword()).getBytes());
        Assert.isTrue(s.equals(one.getPassword()),ResponseEnum.PASSWORD_ERROR);

        //向jwt的负载中存入信息
        Map<String, String> map = new HashMap<>();
        map.put("userId",one.getUserId().toString());
        //如果是商家用户登录，在jwt的负载中存入storeId
        if (form.getRole().equals("seller")){
            Integer storeId = storeInfoMapper.getStoreIdBySellerId(one.getUserId());
            map.put("storeId",storeId.toString());
        }
        map.put("username",one.getUsername());
        map.put("loginRole",form.getRole());
        String token = JWTUtils.getToken(map);
        //生成userVO返回
        UserVO userVO = new UserVO();
        userVO.setUsername(one.getUsername())
                .setNickname(one.getNickname())
                .setIconUrl(one.getIconUrl())
                .setLoginRole(form.getRole())
                .setToken(token);

        return ResultVOUtil.success(userVO);
    }

    /**
     * 用户使用手机号登录(需要账号已绑定手机)
     */
    @PostMapping(value = "/phoneLogin")
    public ResultVO phoneLogin(@Valid @RequestBody PhoneLoginForm form){
        Assert.notBlank(form.getRole(),ResponseEnum.PARAM_NOTNULL);
        //校验手机号格式是否正确
        Assert.isTrue(RegexValidateUtil.checkMobile(form.getPhone()),ResponseEnum.PHONE_ERROR);
        //1.判断手机号是否已绑定
        User one = userService.getByPhone(form.getPhone());
        if (one == null){
            return ResultVOUtil.fail("登录失败，此手机号未绑定账号...");
        }
        //2.验证用户身份权限
        String[] s1 = one.getRole().split("_");
        List<String> roles = Arrays.asList(s1);
        //用户没有对应的身份权限
        if (!roles.contains(form.getRole())){
            throw new EorderException(ResponseEnum.USER_NO_ROLE.getMsg());
        }
        //3.核对验证码
        //从redis中取验证码
        String strCode = (String)redisTemplate.opsForValue().get("eorder" + form.getPhone());
        //比对验证码是否正确
        if (!form.getCode().equals(strCode)){
            return ResultVOUtil.fail("验证码错误或过期，登录失败...");
        }

        //4.向jwt的负载中存入信息后返回
        Map<String, String> map = new HashMap<>();
        map.put("userId",one.getUserId().toString());
        //如果是商家用户登录，在jwt的负载中存入storeId
        if (form.getRole().equals(Constants.SELLER)){
            map.put("storeId",storeInfoMapper.getStoreIdBySellerId(one.getUserId()).toString());
        }
        map.put("username",one.getUsername());
        map.put("loginRole",form.getRole());
        String token = JWTUtils.getToken(map);
        //生成userVO返回
        UserVO userVO = new UserVO();
        userVO.setUsername(one.getUsername())
                .setNickname(one.getNickname())
                .setIconUrl(one.getIconUrl())
                .setLoginRole(form.getRole())
                .setToken(token);

        return ResultVOUtil.success(userVO);
    }


    /**
     * 修改昵称
     */
    @PutMapping("/editNickname/{nickname}")
    public ResultVO editNickname(@RequestHeader("token") String token, @PathVariable String nickname){
        //参数校验
        Assert.notBlank(nickname,ResponseEnum.PARAM_NOTNULL);
        if (nickname.length() > 15){
            return ResultVOUtil.fail("昵称长度超过15...");
        }

        //获取用户id
        Integer userId = TokenUtils.getUserId(token);

        boolean b = userMapper.editNickname(userId, nickname);
        if (!b){
            return ResultVOUtil.fail("修改昵称失败，请重试...");
        }
        return ResultVOUtil.success(null);
    }

    /**
     * 上传头像
     * @return 返回头像地址
     */
    @PostMapping("/uploadIcon")
    public ResultVO uploadIcon(@RequestParam("fileUpload") MultipartFile multipartFile){
        //获取文件名
        String filename = multipartFile.getOriginalFilename();
        //获取文件名后缀
        String suffix = filename.substring(filename.lastIndexOf("."));
        //重新生成文件名
        filename = UUID.randomUUID() + suffix;

        try {
            //将图片保存到文件夹里
            multipartFile.transferTo(new File(filePath + filename));
        } catch (IOException e) {
            e.printStackTrace();
            return ResultVOUtil.fail("上传图片失败...");
        }
        return ResultVOUtil.success(filePath+filename);
    }


    /**
     * 修改头像
     */
    @PostMapping("/editIcon")
    public ResultVO editIcon(@RequestHeader("token") String token, @Valid @RequestBody StoreIconForm form){
        //获取用户id
        Integer userId = TokenUtils.getUserId(token);

        boolean b = userMapper.editIcon(userId, form.getStoreIcon());
        if (!b){
            return ResultVOUtil.fail("修改头像失败，请重试...");
        }
        return ResultVOUtil.success(null);
    }

    /**
     *  获取手机验证码
     *  type="login"  表示用户登录时获取验证码
     *  type="bind"   表示用户绑定/解绑手机号时获取验证码
     */
    @GetMapping("/getCode/{type}/{phone}")
    public ResultVO getCode(@PathVariable String type,@PathVariable String phone){
        //type参数不能为空
        Assert.notBlank(type,ResponseEnum.PARAM_NOTNULL);
        //校验手机号格式是否正确
        Assert.isTrue(RegexValidateUtil.checkMobile(phone),ResponseEnum.PHONE_ERROR);

        //如果是登录时获取验证码，需要判断手机号是否已绑定
        if (type.equals("login")){
            //判断该手机号是否已被绑定，如果没绑定，就不发验证码
            User byPhone = userService.getByPhone(phone);
            if (byPhone == null){
                return ResultVOUtil.fail("该手机号未绑定账号，发送失败...");
            }
        }

        try {
            //发送验证码
            String code = userService.sendSms(phone, akId, akSecret);
            //将验证码存入redis
            String key = "eorder" + phone;
            //验证码存入redis，有效期5分钟
            redisTemplate.opsForValue().set(key,code,5, TimeUnit.MINUTES);
        } catch (Exception e) {
            e.printStackTrace();
            return ResultVOUtil.fail("发送失败，请重试...");
        }
        return ResultVOUtil.success(null);
    }

    /**
     * 绑定手机号（一个手机号只能绑定一个账号）
     */
    @PostMapping("/bindPhone")
    public ResultVO bindPhone(@RequestHeader("token") String token, @Valid @RequestBody PhoneLoginForm form){
        //校验手机号格式是否正确
        Assert.isTrue(RegexValidateUtil.checkMobile(form.getPhone()),ResponseEnum.PHONE_ERROR);
        //获取用户id
        Integer userId = TokenUtils.getUserId(token);
        //判断用户是否已经绑定过手机号
        User user = userService.getById(userId);
        if (user.getPhone() != null){
            return ResultVOUtil.fail("绑定失败，账户已绑定过手机号，请先解绑...");
        }
        //判断该手机号是否已被其它账号绑定
        User byPhone = userService.getByPhone(form.getPhone());
        if (byPhone != null){
            return ResultVOUtil.fail("绑定失败，该手机号已被其它账号绑定...");
        }
        //从redis中取验证码
        String strCode = (String)redisTemplate.opsForValue().get("eorder" + form.getPhone());
        //比对验证码是否正确
        if (!form.getCode().equals(strCode)){
            return ResultVOUtil.fail("验证码错误，绑定失败...");
        }
        //绑定手机号
        boolean b = userMapper.editPhone(userId, form.getPhone());
        if (!b){
            return ResultVOUtil.fail("绑定手机号失败，请重试...");
        }
        return ResultVOUtil.success(null);
    }

    /**
     * 解绑手机
     */
    @PostMapping("/unbindPhone")
    public ResultVO unbindPhone(@RequestHeader("token") String token,@Valid @RequestBody PhoneLoginForm form){
        //校验手机号格式是否正确
        Assert.isTrue(RegexValidateUtil.checkMobile(form.getPhone()),ResponseEnum.PHONE_ERROR);

        //获取用户id
        Integer userId = TokenUtils.getUserId(token);

        //判断数据库中用户的手机号和要解绑的手机号是否一致
        User user = userService.getById(userId);

        if (user.getPhone()==null&&user.getPhone()==""){
            return ResultVOUtil.fail("尚未绑定手机号码");
        }
        if (!form.getPhone().equals(user.getPhone())){
            return ResultVOUtil.fail("解绑失败，解绑的手机号和已绑定的不一致...");
        }

        //从redis中取验证码
        String strCode = (String)redisTemplate.opsForValue().get("eorder" + form.getPhone());
        //比对验证码是否正确
        if (!form.getCode().equals(strCode)){
            return ResultVOUtil.fail("验证码错误，解绑失败...");
        }

        //解绑手机号，phone置为null
        boolean b = userMapper.editPhone(userId, null);
        if (!b){
            return ResultVOUtil.fail("绑定手机号失败，请重试...");
        }
        return ResultVOUtil.success(null);
    }

    /**
     * 重置密码
     * 以原密码，新密码形式
     */
    @PostMapping("/resetPw")
    public ResultVO resetPw(@RequestHeader("token") String token,@Valid @RequestBody ResetPwForm form){
        //获取用户id
        Integer userId = TokenUtils.getUserId(token);

        User user = userService.getById(userId);
        //对提交的原密码进行md5加盐加密
        String md5 = DigestUtils.md5DigestAsHex((user.getSalt() + form.getBeforePw()).getBytes());
        if (!md5.equals(user.getPassword())){
            return ResultVOUtil.fail("重置失败，密码错误...");
        }
        //重置密码
        String now = DigestUtils.md5DigestAsHex((user.getSalt() + form.getNowPw()).getBytes());
        user.setPassword(now);
        boolean b = userService.updateById(user);
        if (!b){
            return ResultVOUtil.fail("重置密码失败，请重试...");
        }
        return ResultVOUtil.success(null);
    }


    /**
     *
     * @author shishuqian
     * @desc  获取个人信息
     */
    @GetMapping("/userInfo")
    public ResultVO userInfo(@RequestHeader("token") String token){

        Integer userId = TokenUtils.getUserId(token);

        User user = userService.getById(userId);
        String[] s = user.getRole().split("_");
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user,userInfoVO);
        userInfoVO.setRoles(s);
        return ResultVOUtil.success(userInfoVO);

    }

    //测试token过期
    //@GetMapping("/test")
    //public ResultVO test(){
    //    ResultVO<Object> vo = new ResultVO<>();
    //    vo.setCode(-2);
    //    vo.setMsg("test");
    //    return vo;
    //}


}
